﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace ZQNB.Common.Attributes
{
    public class AssemblyAttributeHelperTool
    {
        private static Dictionary<Type, Object> dic = new Dictionary<Type, object>();
        private static bool _needReload = true;
        private static readonly object Lock = new object();

        static AssemblyAttributeHelperTool()
        {
            AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
        }

        static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
        {
            _needReload = true;
        }

        /// <summary>
        /// 迟加载依赖程序集，且内部做了缓存处理
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static List<T> FindMetasInAppDomain<T>()
        {
            lock (Lock)
            {
                List<T> result = new List<T>();
                object value = null;
                Type attrType = typeof(T);
                bool hasValue = dic.TryGetValue(attrType, out value);
                if (hasValue)
                {
                    if (_needReload)
                    {
                        List<T> list = new List<T>();
                        Assembly[] assemblys = AppDomain.CurrentDomain.GetAssemblies();
                        foreach (var item in assemblys)
                        {
                            List<T> temp = FindMetasInAssembly<T>(item);
                            list.AddRange(temp);
                        }
                        if (list.Count > 0)
                        {
                            dic.Remove(attrType);
                            dic.Add(attrType, list);
                        }
                    }
                    result = (List<T>)value;
                }
                else
                {
                    List<T> list = new List<T>();
                    Assembly[] assemblys = AppDomain.CurrentDomain.GetAssemblies();
                    foreach (var item in assemblys)
                    {
                        List<T> temp = FindMetasInAssembly<T>(item);
                        list.AddRange(temp);
                    }
                    if (list.Count > 0)
                    {
                        dic.Remove(attrType);
                        dic.Add(attrType, list);
                    }
                    result = list;
                }

                _needReload = false;
                return result;
            }
        }

        public static List<T> FindMetasInAssembly<T>(params Assembly[] assemblies)
        {
            List<T> list = new List<T>();
            Type attrType = typeof(T);
            foreach (var assembly in assemblies)
            {
                object[] metasInAssembly = assembly.GetCustomAttributes(attrType, true);
                if (metasInAssembly.Length > 0)
                {
                    list.AddRange(metasInAssembly.Select(item => (T)item));
                }
            }
            return list;
        }

        public static List<T> FindMetasInClass<T>(params Assembly[] assemblies)
        {
            List<T> list = new List<T>();
            Type attrType = typeof(T);
            foreach (var assembly in assemblies)
            {
                Type[] types = assembly.GetTypes();
                foreach (var type in types)
                {
                    object[] metasInAssembly = type.GetCustomAttributes(attrType, true);
                    if (metasInAssembly.Length > 0)
                    {
                        list.AddRange(metasInAssembly.Select(item => (T)item));
                    }
                }
            }
            return list;
        }

        public static List<T> FindMetasInMethod<T>(params Assembly[] assemblies)
        {
            List<T> list = new List<T>();
            Type attrType = typeof(T);
            foreach (var assembly in assemblies)
            {
                Type[] types = assembly.GetTypes();
                foreach (var type in types)
                {
                    object[] metasInClass = type.GetCustomAttributes(typeof(HasMetasInMethodAttribute), true);
                    if (metasInClass.Length > 0)
                    {
                        var methods = type.GetMethods();
                        foreach (var methodInfo in methods)
                        {
                            object[] metasInAssembly = methodInfo.GetCustomAttributes(attrType, true);
                            if (metasInAssembly.Length > 0)
                            {
                                list.AddRange(metasInAssembly.Select(item => (T)item));
                            }
                        }
                    }
                }
            }
            return list;
        }
    }
}
